package Thread;

/**
 * @Author: 逸尘风清
 * @Date: 2021/4/25
 * @Time: 18:56
 * 描述：
 */

import java.sql.Time;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 页面中的代码不一定都能运行，有的只是片段记录
 */
public class ThreadNotes {

    /**
     * 1.什么是线程:
     * 线程的通俗概念： 一个进程里面不同的执行路径，就是一个线程
     */
    public static class T1 extends Thread {
        public void run(){
            for (int i = 0; i <= 10; i++) {
                try {
                    TimeUnit.MICROSECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("T1");
            }
        }
    }
    // public static void main(String[] args) {
    //     // new T1().run();    // 先执行完这个线程再执行下面的代码
    //     new T1().start();  // 开始线程，和下面的代码同时执行
    //     for (int i = 0; i <= 10; i++) {
    //         try {
    //             TimeUnit.MICROSECONDS.sleep(1);
    //         } catch (InterruptedException e) {
    //             e.printStackTrace();
    //         }
    //         System.out.println("main");
    //     }
    // }

    /**
     * 2.线程的创建方式:
     */
    // 线程的创建方式一
    public static class T2 extends Thread {
        public void run(){
            for (int i = 0; i <= 10; i++) {
                try {
                    TimeUnit.MICROSECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("T2");
            }
        }
    }

    // 线程的创建方式二
    public static class T3 implements Runnable {
        public void run(){
            for (int i = 0; i <= 10; i++) {
                try {
                    TimeUnit.MICROSECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("T3");
            }
        }
    }

    // public static void main(String[] args) {
    //     new T2().start();               // T2线程启动方式
    //     new Thread( new T3()).start();  // T3线程启动方式
    //     new Thread(()->{                // lambda创建线程并启动
    //         System.out.println("lambda");
    //     }).start();
    // }

    /**
     * 面试题：请你告诉我线程启动的三种方式：
     * 1.继承Thread
     * 2.实现Runnable接口
     * 3.Executors.newCachedThreadPool() 使用线程池方式，而并非上面的lambda方式
     */

    /**
     * 线程sleep：是当前线程进行睡眠，在睡眠期间其他线程可以进入CPU运行
     * 线程yield: 是指当前线程执行到中途主动退出，重新进入线程等待队列中
     * 线程join：是指当前线程执行到中途，突然加入其他线程，那么需要等待加入的线程执行完成后再执行当前线程
     */

    /**
     *
     * synchronized是Java中的关键字，是一种同步锁。它修饰的对象有以下几种：
     * 1. 修饰一个代码块，被修饰的代码块称为同步语句块，其作用的范围是大括号{}括起来的代码，作用的对象是调用这个代码块的对象；
     * 2. 修饰一个方法，被修饰的方法称为同步方法，其作用的范围是整个方法，作用的对象是调用这个方法的对象；
     * 3. 修改一个静态的方法，其作用的范围是整个静态方法，作用的对象是这个类的所有对象；
     * 4. 修改一个类，其作用的范围是synchronized后面括号括起来的部分，作用主的对象是这个类的所有对象。
     */

    static class SyncThread implements Runnable {
        private int count;
        public SyncThread() {
            count = 0;
        }
        public void run() {
            // synchronized (this){   // 开启则线程被锁定，需要完成后才能执行下一个线程，开启和不开启运行结果很明显的差异
                for (int i = 0; i < 5; i++) {
                    try {
                        System.out.println("线程名:"+Thread.currentThread().getName() + ":" + (count++));
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            // }
        }
    }

    // public static void main(String[] args) {
    //     System.out.println("使用关键字synchronized");
    //     SyncThread syncThread = new SyncThread();
    //     Thread thread1 = new Thread(syncThread, "SyncThread1");
    //     Thread thread2 = new Thread(syncThread, "SyncThread2");
    //     thread1.start();
    //     thread2.start();
    // }

    public static class T5 {
        public synchronized void m1() {
            System.out.println("线程名:" + Thread.currentThread().getName() + ":" + "m1 start");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程名:" + Thread.currentThread().getName() + ":" + "m1 end");
        }

        public void m2() {
            try {
                System.out.println("线程名:" + Thread.currentThread().getName() + ":" +  "m2 start");
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    // public static void main(String[] args) {
    //     T5 t = new T5();
    //     new Thread(t::m1,"Thread1").start();
    //     new Thread(t::m2,"Thread1").start();
    // }
    /**
     * 输出结果：
     * 线程名:Thread1:m1 start
     * 线程名:Thread1:m2 start
     * 线程名:Thread1:m1 end
     * 由于m2方法没有被加锁，所以可以正常被执行，不会被m1的加锁限制
     */

    /**
     * 给银行账号设置金额加锁，获取金额不加锁
     */
    public static class T6{
        private String name;
        private double money;
        public synchronized void set(String name, Double money){
            this.name = name;
            try {
                Thread.sleep(2000);  //单位ms
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.money = money;
        }
        public /*synchronized*/ double getMoney(String name){
            return this.money;
        }

        public static void main(String[] args) {
            T6 t = new T6();
            new Thread(() -> t.set("zhangsan", 100.00)).start();

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(t.getMoney("zhangsan"));

            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(t.getMoney("zhangsan"));
        }
        /**
         * 输出结果：
         * 1.如果set加锁，而get没有加锁。那么输出值时： 0.0  100.0
         * 原因：当主程序启动时先执行set方法，set需要2秒完成，而main程序在set执行过程中等待1秒就立即执行get方法，因为get没有被加锁不需要等待set执行完才执行。这时候set还没有执行完，所以get得到的是初始值0.0
         * 2.如果set和get都被加锁。那么输出值时： 100.0  100.0
         * 原因：当主程序启动时先执行set方法，set需要2秒完成，而因为get也被加锁，需要等待set执行完才执行。
         */
    }

    public static class T7 {
        public synchronized void m1() {
            System.out.println("m1 start");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            m2();
            System.out.println("m1 end");
        }

        public synchronized void m2() {
            try {
                System.out.println("m2 start");
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public static void main(String[] args) {
            T7 t = new T7();
            t.m1();
        }

        /**
         * synchronized可重入性，在m1中可以调用m2，虽然m2是被加锁了的。但是它们是同一把锁，锁的都是this。为什么呢？
         * 原因：
         * public synchronized void m2() {
         *      // todo
         * }  写法等同于
         * public void m2{
         *     synchronized(this){   // 这里锁的就是this
         *          // todo
         *     }
         * }
         * 当m1执行m2时，检测到 m2也是需要同一把锁，所以就放行执行，这个过程就叫重入
         */
    }

    public static class T8 {
        public synchronized void m() {
            System.out.println("parent start");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("parent end");
        }

        public static void main(String[] args) {
            T9 t = new T9();
            t.m();
        }
    }

    public static class T9 extends T8 {
        @Override
        public synchronized void m() {
            System.out.println("child start");
            super.m();
            System.out.println("child end");
        }
    }
    /**
     * 子类继承父类，加的还是同一个把锁
     */

    /**
     * synchronized对对象进行加锁
     * 模拟异常锁
     * 特性：发生异常后，锁会默认被释放
     */
    public static class T10 {
        int count = 0;
        synchronized void m(){
            System.out.println(Thread.currentThread().getName() + "m start");
            while (true) {
                count ++;
                System.out.println(Thread.currentThread().getName() + "count = " + count);
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(count == 5){
                    int i = 1 / 0;  // 模拟抛出异常
                    System.out.println(i);
                }
            }
        }

        public static void main(String[] args) {
            T10 t = new T10();
            Runnable r = new Runnable() {
                @Override
                public void run() {
                    t.m();
                }
            };
            new Thread(r,"t1").start();

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(r,"t2").start();
        }
        /**
         * 以上例子当第一个线程抛出异常后才会执行第二个线程，如果第一个线程没有抛出异常，则永远执行第一个线程，因为写的是个死循环。
         * 也证明了，抛出异常后锁会自动被释放，如果不被释放，那么第二个线程也是无法获取到锁进行执行的
         */
    }

    /**
     * synchronized
     * 自旋锁：占用CPU   场景：执行 加锁 的代码时间短选择， 线程数少
     * OS系统锁：不占CPU  执行时间长的时候尽量用系统锁，  线程数多也用系统锁
     *
     * 锁升级：偏向锁 =》 自旋锁（转圈等待cpu调用） =》 重量级锁（向os申请的）
     */
}


